-module(worm).
-export([init1/2, init1/1]).
-export([init2/2, init2/1]).
-include("cell_records.hrl").

init1(_Id, Worm) ->
   spawn(worm, init1, [Worm]).

init1(#worm{id = Id, cpid = CPid} = State) ->
  CPid ! {occupy, self()},
  receive
    stop -> exit("stopped while initiating");
    {occup_ok, _Pid} ->
      ok;
    {error, already_taken, _Pid} ->
      props ! {dead, Id, self()},
      exit("unable to occupy cell")
  end,
  CPid ! {get_position, self()},
  receive
    stop -> exit("stopped while initiating");
    {position, {X, Y}} ->
      #worm{spawn_weight = SpawnW, min_weight = MinW, weight = W, speed = Sp} = State,
      connector ! {new_thing, 0, Id, X, Y, SpawnW, MinW, W, Sp},
      loop(State#worm{position = {X, Y}})
  end.


init2(_Id, Worm) ->
   spawn(worm, init2, [Worm]).

init2(#worm{id = Id, position = {X,Y}} = State) ->
  control ! {find, X, Y, self()},
  receive
    stop -> exit("stopped while initiating");
    {position, X, Y, CPid} ->
      CPid ! {occupy, self()},
      receive
        stop -> exit("stopped while initiating");
        {occup_ok, _Pid} ->
          ok;
        {error, already_taken, _Pid} ->
          props ! {dead, Id, self()},
          exit("unable to occupy cell");
        Msg ->
          io:format("Msggg'~p'~n", [Msg])
      end,
      #worm{spawn_weight = SpawnW, min_weight = MinW, weight = W, speed = Sp} = State,
      connector ! {new_thing, 0, Id, X, Y, SpawnW, MinW, W, Sp},
      pause(State#worm{cpid = CPid})
  end.


%% worm loop
loop(#worm{speed = Speed, age = Age} = State) ->
  receive
    pause ->
      pause(State);
    stop -> exit("stopped");
    Msg -> % ignore wrong messages
      io:format("Worm: Wrong message: '~p'~n", [Msg]),
      loop(State)
  after Speed ->
    #worm{eat_rate = ER, weight = W} = State,
    %io:format("Worm:~p~n", [State]),
    spawn_or_die_biach(State#worm{age = Age + 1, weight = W - trunc(ER/2)})
  end.


%% 0. umrzyj, jeśli musisz
spawn_or_die_biach(#worm{id = Id, cpid = CPid, weight = W, min_weight = MNW, age = Age}) when ((W =< MNW) or (Age >= 40)) ->
  CPid ! {set, occupant, none},
  props ! {dead, self(), Id};
%% 1. rozmnóż się, jeśli możesz (masz wystarczającą masę)
spawn_or_die_biach(#worm{weight = W, spawn_weight = SW} = State) when W >= SW ->
  spawn_biach(State, get_nb_count(State));
%% 2. w przeciwnym razie jedz, jeśli jest jedzenie na twoim polu
spawn_or_die_biach(State) ->
  eat_biach(State).
%% 3. w przeciwnym razie przejdź na sąsiednie pole


spawn_biach(State, Count) ->
  {CPid, _Veg} = room_biach(0, Count, {none, 0}),
  if
    CPid /= none ->
      #worm{spawn_weight = SW, min_weight= MW, weight = W, eat_rate = ER, speed = S} = State,
      NewWeight = MW + (W - MW)/5,
      {SW1, MW1, W1, ER1, S1} = new_features(SW, MW, NewWeight, ER, S),
      props ! {the_thing, #worm{cpid = CPid, spawn_weight = SW1, min_weight= MW1, weight = W1, eat_rate = ER1, speed = S1}},
      loop(State#worm{weight = floor(NewWeight)});
    true ->
      move_biach(State)
  end.

%% eat_biach(State)
eat_biach(#worm{cpid = CPid, eat_rate = ER} = State) ->
  CPid ! {eat_veg, ER},
  receive
    stop -> exit("stopped while eating");
    {here_you_go, Veg} ->
      #worm{weight = W, id = Id} = State,
      connector ! {fat_boy, Id, W + Veg},
      loop(State#worm{weight = W + Veg});
    not_enough ->
      move_biach(State)
  end.

%% move_biach(State)
move_biach(#worm{id = Id, cpid = CPid} = State) ->
  NB = get_nb_count(State),
  {NewCPid, _Veg} = room_biach(0, NB, {none, 0}),
  if
    NewCPid /= none ->
      NewCPid ! {occupy, self()},
      receive
        stop -> exit("stopped while moving");
        {error, already_taken, _Pid} -> loop(State);
        {occup_ok, _Pid} ->
          CPid ! {set, occupant, none},
          NewCPid ! {get_position, self()},
          receive
            stop -> exit("stopped while moving");
            {position, {X, Y}} ->
              connector ! {moved, Id, X, Y},
              loop(State#worm{position = {X, Y}, cpid = NewCPid})
          end
      end;
    true ->
      io:format("nie znaleziono odpowiedniego miejsca do przejścia!~n",[]),
      loop(State)
  end.

%% spawn_biach(State) and move_biach(State) helper
%% use to find the best not occupied cell in neighborhood
%% room_biach(Received, Count, Best)
room_biach(Received, Count, Best) ->
  receive
    stop -> exit("stopped while getting space");
    {veg_occupation_state, Veg, Oc, CPid} ->
      NewBest = better_biach_biach(Best, Veg, Oc, CPid),
      if
        Received + 1 == Count -> % koniec wiadomości, zdecyduj co dalej
          NewBest;
        true ->
          room_biach(Received + 1, Count, NewBest)
      end
  end.

%% spawn_biach(Received, Count, Best) helper
%% better({Pid, Veg}, Veg, Oc, CPid) ->
better_biach_biach({_BPid, BVeg}, CVeg, Oc, CPid) when ((Oc == none) and (CVeg > BVeg)) ->
  {CPid, CVeg};
better_biach_biach({BPid, BVeg}, _CVeg, _Oc, _CPid) ->
  {BPid, BVeg}.

get_nb_count(#worm{cpid = CPid}) ->
  CPid ! {get_nbors, self()},
  receive
    {nb_count, Count} -> Count;
    stop -> exit("stopped while waiting for nb count")
  end.

pause(State) ->
  receive
    stop -> exit("stopped while paused");
    play -> loop(State)
  end.

new_features(SW, MW, W, ER, S) ->
  R = gauss:dist(),
  SW1 = feature(1, 30000, SW, R, 150),
  MW1 = feature(1, 30000, MW, R, 150),
  W1 = feature(1, 30000, W, R, 150),
  ER1 = feature(1, 30000, ER, R, 100),
  S1 = feature(200, 4000, S, R, 200),
  {SW1, MW1, W1, ER1, S1}.

feature(Min, Max, Val, Rand, Ch) ->
  New = floor(Val + Rand()*Ch),
  if
    New > Max -> Max;
    New < Min -> Min;
    true -> New
  end.

floor(X) ->
    T = erlang:trunc(X),
    case (X - T) of
        Neg when Neg < 0 -> T - 1;
        Pos when Pos > 0 -> T;
        _ -> T
    end.

